package com.platform.common.util.encrypt;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author liuli
 *它是第一个既能用于数据加密也能用于数字签名的算法
 *RSA同时有两把钥匙，公钥与私钥。同时支持数字签名
 *数字签名的意义在于，对传输过来的数据进行校验。确保数据在传输工程中不被修改
 *流程分析： 
	1.甲方构建密钥对儿，将公钥公布给乙方，将私钥保留。
	2.甲方使用私钥加密数据，然后用私钥对加密后的数据签名，发送给乙方签名以及加密后的数据；
	    乙方使用公钥、签名来验证待解密数据是否有效，如果有效使用公钥对数据解密。
	3.乙方使用公钥加密数据，向甲方发送经过加密后的数据；甲方获得加密数据，通过私钥解密
 */
public abstract class RSACoder extends Coder {
	  static String publicKey;  
	  static String privateKey;  
	public static void main(String[] args) {
		try {
			Map<String, Object> keyMap = RSACoder.initKey();
	        publicKey = RSACoder.getPublicKey(keyMap);
	        privateKey = RSACoder.getPrivateKey(keyMap);
	       // System.err.println("公钥:" + publicKey);  
	       // System.err.println("私钥:" + privateKey); 
	        
	        System.err.println("====公钥加密——私钥解密");
	        String inputStr = "abc";  
	        byte[] data = inputStr.getBytes();  
	        byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);
	        byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,privateKey);
	        String outputStr = new String(decodedData);  
	        System.err.println("加密前: " + inputStr + "||" + "解密后: " + outputStr);  
	        
	        System.err.println("====私钥加密——公钥解密");  
	        encodedData = RSACoder.encryptByPrivateKey(data, privateKey);
	        decodedData = RSACoder.decryptByPublicKey(encodedData, publicKey);
	        outputStr = new String(decodedData);  
	        System.err.println("加密前: " + inputStr + "||" + "解密后: " + outputStr);  
	        
	        
	        System.err.println("私钥签名——公钥验证签名");  
	        // 产生签名  
	        String sign = RSACoder.sign(encodedData, privateKey);
	        System.err.println("签名:\r" + sign);  
	        // 验证签名  
	        boolean status = RSACoder.verify(encodedData, publicKey, sign);
	        System.err.println("状态:\r" + status);  
	        
	        
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
	 
	public static final String KEY_ALGORITHM = "RSA";  
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";  
    private static final String PUBLIC_KEY = "RSAPublicKey";  
    private static final String PRIVATE_KEY = "RSAPrivateKey";  
    /** 
     * 用私钥对信息生成数字签名 
     * @param data  加密数据 
     * @param privateKey  私钥   
     * @return 
     * @throws Exception 
     */  
    public static String sign(byte[] data, String privateKey) throws Exception {  
        // 解密由base64编码的私钥  
        byte[] keyBytes = decryptBASE64(privateKey);  
        // 构造PKCS8EncodedKeySpec对象  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);    
        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);    
        // 取私钥匙对象  
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);   
        // 用私钥对信息生成数字签名  
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initSign(priKey);  
        signature.update(data);  
        return encryptBASE64(signature.sign());  
    }  
  
    /** 
     * 校验数字签名 
     *  
     * @param data 加密数据 
     * @param publicKey  公钥 
     * @param sign 数字签名 
     * @return 校验成功返回true 失败返回false 
     * @throws Exception 
     *  
     */  
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {  
        // 解密由base64编码的公钥  
        byte[] keyBytes = decryptBASE64(publicKey);   
        // 构造X509EncodedKeySpec对象  
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);    
        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);    
        // 取公钥匙对象  
        PublicKey pubKey = keyFactory.generatePublic(keySpec);   
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initVerify(pubKey);  
        signature.update(data);  
        // 验证签名是否正常  
        return signature.verify(decryptBASE64(sign));  
    }  
  
    /** 
     * 解密<br> 
     * 用私钥解密 
     *  
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);  
        // 取得私钥  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);    
        // 对数据解密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);    
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 解密<br> 
     * 用公钥解密 
     *  
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);    
        // 取得公钥  
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(x509KeySpec);    
        // 对数据解密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);    
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 加密<br> 
     * 用公钥加密 
     *  
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {  
        // 对公钥解密  
        byte[] keyBytes = decryptBASE64(key);    
        // 取得公钥  
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(x509KeySpec);   
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);   
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 加密<br> 
     * 用私钥加密 
     *  
     * @param data 
     * @param key 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {  
        // 对密钥解密  
        byte[] keyBytes = decryptBASE64(key);   
        // 取得私钥  
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 取得私钥 
     * @param keyMap 
     * @return 
     * @throws Exception 
     */  
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {  
        Key key = (Key) keyMap.get(PRIVATE_KEY);  
        return encryptBASE64(key.getEncoded());  
    }  
  
    /** 
     * 取得公钥 
     * @param keyMap 
     * @return 
     * @throws Exception 
     */  
    public static String getPublicKey(Map<String, Object> keyMap)  throws Exception {  
        Key key = (Key) keyMap.get(PUBLIC_KEY);  
        return encryptBASE64(key.getEncoded());  
    }  
  
    /** 
     * 初始化密钥 
     * @return 
     * @throws Exception 
     */  
    public static Map<String, Object> initKey() throws Exception {  
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);  
        keyPairGen.initialize(1024);  
        KeyPair keyPair = keyPairGen.generateKeyPair();  
        // 公钥  
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();    
        // 私钥  
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   
        Map<String, Object> keyMap = new HashMap<String, Object>(2);  
        keyMap.put(PUBLIC_KEY, publicKey);  
        keyMap.put(PRIVATE_KEY, privateKey);  
        return keyMap;  
    }  
}
